//
//  PLPattern.m
//  Plankton
//
//  Created by Marc Schlichte on 15.06.09.
//  Copyright 2009 Framework Labs. All rights reserved.
//

#import "PLPattern.h"


#pragma mark target action handler interfaces

@interface PLTargetActionPair : NSObject
{
    id  target;
    SEL action;
}
@end

@interface PLTargetActionMessageHandler : PLTargetActionPair <PLMessageHandler>

+ (PLTargetActionMessageHandler *)handlerWithTarget:(id)target action:(SEL)action;

@end

@interface PLTargetActionTimeoutHandler : PLTargetActionPair <PLTimeoutHandler>

+ (PLTargetActionTimeoutHandler *)handlerWithTarget:(id)target action:(SEL)action;

@end

#pragma mark target action handler implementation

@implementation PLTargetActionPair

- (id)initWithTarget:(id)target_ action:(SEL)action_
{
    if (self = [super init]) {
        target = target_; // weak-ref
        action = action_;
    }
    return self;
}

@end


@implementation PLTargetActionMessageHandler

+ (PLTargetActionMessageHandler *)handlerWithTarget:(id)target action:(SEL)action
{
    return [[[PLTargetActionMessageHandler alloc] initWithTarget:target action:action] autorelease];
}

- (void)handleMessage:(id)message forPattern:(PLPattern *)pattern
{
    [target performSelector:action withObject:message withObject:pattern];
}

@end


@implementation PLTargetActionTimeoutHandler

+ (PLTargetActionTimeoutHandler *)handlerWithTarget:(id)target action:(SEL)action
{
    return [[[PLTargetActionTimeoutHandler alloc] initWithTarget:target action:action] autorelease];
}

- (void)handleTimeoutForPattern:(PLPattern *)pattern;
{
    [target performSelector:action withObject:pattern];
}

@end


#pragma mark block handlers

#if NS_BLOCKS_AVAILABLE

@interface PLBlockMessageHandler : NSObject <PLMessageHandler>
{
	void (^block)(id message, PLPattern *pattern);
}

- (id)initWithBlock:(void (^)(id message, PLPattern *pattern))aBlock;
					 
@end

@implementation PLBlockMessageHandler

- (id)initWithBlock:(void (^)(id message, PLPattern *pattern))aBlock 
{
	if (self = [super init]) {
		block = [aBlock copy];
	}
	return self;
}

- (void)dealloc 
{
	[block release];
	[super dealloc];
}

- (void)handleMessage:(id)message forPattern:(PLPattern *)pattern
{
	block(message, pattern);
}

@end


@interface PLBlockTimeoutHandler : NSObject <PLTimeoutHandler>
{
	void (^block)(PLPattern *pattern);
}

- (id)initWithTimeoutBlock:(void (^)(PLPattern *))aBlock;

@end


@implementation PLBlockTimeoutHandler

- (id)initWithTimeoutBlock:(void (^)(PLPattern *))aBlock
{
	if (self = [super init]) {
		block = [aBlock copy];
	}
	return self;
}

- (void) dealloc
{
	[block release];
	[super dealloc];
}


- (void)handleTimeoutForPattern:(PLPattern *)pattern;
{
	block(pattern);
}

@end

#endif


#pragma mark patterns

@interface PLPredicatePattern : PLPattern
{
    NSPredicate *predicate;
}

- (PLPredicatePattern *)initWithPredicate:(NSPredicate *)pred;

@end


@interface PLPattern ()

@property(nonatomic, retain) NSString *receiver;

@end


@implementation PLPattern

@synthesize userInfo;

+ (PLPattern *)patternAny
{
    return [[[PLPattern alloc] init] autorelease];
}

+ (PLPattern *)patternWithPredicate:(NSPredicate *)predicate
{
    return [[[PLPredicatePattern alloc] initWithPredicate:predicate] autorelease];
}

+ (PLPattern *)patternWithPredicateFormat:(NSString *)predicateFormat, ...
{
    va_list ap;
    va_start(ap, predicateFormat);
    PLPattern *res = [self patternWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:ap]];
    va_end(ap);
    return res;
}

- (void)dealloc
{
    [userInfo release];
    [timeoutTimer release];
    [timeoutHandler release];
    [messageHandler release];
	[receiver release];
    [super dealloc];
}

- (PLPattern *)setMessageHandler:(id<PLMessageHandler>)handler
{
    [messageHandler autorelease];
    messageHandler = [handler retain];
    return self;
}

- (id<PLMessageHandler>)messageHandler
{
    return messageHandler;
}

- (PLPattern *)setMessageTarget:(id)target action:(SEL)action
{
    return [self setMessageHandler:[PLTargetActionMessageHandler handlerWithTarget:target action:action]];
}

- (PLPattern *)setTimeoutHandler:(id<PLTimeoutHandler>)timeout after:(NSTimeInterval)interval
{
    [timeoutHandler autorelease];
    timeoutHandler = [timeout retain];
    timeoutInterval = interval;
    return self;
}

- (id<PLTimeoutHandler>)timeoutHandler
{
    return timeoutHandler;
}

- (PLPattern *)setTimeoutTarget:(id)target action:(SEL)action after:(NSTimeInterval)interval
{
    return [self setTimeoutHandler:[PLTargetActionTimeoutHandler handlerWithTarget:target action:action] after:interval];
}

#if NS_BLOCKS_AVAILABLE

- (PLPattern *)setMessageBlock:(void (^)(id message, PLPattern *pattern))block 
{
	NSParameterAssert(block);
	return [self setMessageHandler:[[[PLBlockMessageHandler alloc] initWithBlock:block] autorelease]];
}

- (PLPattern *)setTimeoutBlock:(void (^)(PLPattern *))block after:(NSTimeInterval)interval
{
	NSParameterAssert(block);
	return [self setTimeoutHandler:[[[PLBlockTimeoutHandler alloc] initWithTimeoutBlock:block] autorelease] after:interval];
}

#endif

- (BOOL)matches:(id)message
{
    return YES; // this is for the any case, overwrite in subclasses!
}

/// Internal methods

@synthesize receiver;

- (NSTimeInterval)timeoutInterval
{
    return timeoutInterval;
}

- (void)setTimeoutTimer:(NSTimer *)timer
{
    [timeoutTimer autorelease];
    timeoutTimer = [timer retain];
}

- (void)invalidate
{
    [timeoutTimer invalidate];
}

@end


@implementation PLPredicatePattern

- (PLPredicatePattern *)initWithPredicate:(NSPredicate *)pred
{
    if (self = [super init]) {
        predicate = [pred copy];
    }
    return self;
}

- (void)dealloc
{
    [predicate release];
    [super dealloc];
}

- (BOOL)matches:(id)message
{
    @try {
        return [predicate evaluateWithObject:message];
    }
    @catch (NSException *e) {
    }
    return NO;
}

@end

